package com.castle.fortress.admin.shiro.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.castle.fortress.admin.core.constants.GlobalConstants;
import com.castle.fortress.admin.log.dto.LogSmsDto;
import com.castle.fortress.admin.log.entity.LogSmsEntity;
import com.castle.fortress.admin.log.enums.LogSmsStatusEnum;
import com.castle.fortress.admin.log.enums.LogSmsTypeEnum;
import com.castle.fortress.admin.log.service.LogSmsService;
import com.castle.fortress.admin.member.dto.MemberDto;
import com.castle.fortress.admin.member.entity.MemberEntity;
import com.castle.fortress.admin.member.service.MemberService;
import com.castle.fortress.admin.message.sms.service.SmsService;
import com.castle.fortress.admin.shiro.entity.CastleUserDetail;
import com.castle.fortress.admin.shiro.service.DataAuthService;
import com.castle.fortress.admin.system.entity.SysRole;
import com.castle.fortress.admin.system.entity.SysUser;
import com.castle.fortress.admin.system.service.SysMenuService;
import com.castle.fortress.admin.system.service.SysRoleService;
import com.castle.fortress.admin.system.service.SysUserService;
import com.castle.fortress.admin.utils.HttpUtil;
import com.castle.fortress.admin.utils.PwdUtil;
import com.castle.fortress.admin.utils.TokenUtil;
import com.castle.fortress.admin.utils.WebUtil;
import com.castle.fortress.common.entity.RespBody;
import com.castle.fortress.common.enums.DataPermissionPostEnum;
import com.castle.fortress.common.enums.UserTypeEnum;
import com.castle.fortress.common.enums.YesNoEnum;
import com.castle.fortress.common.exception.BizException;
import com.castle.fortress.common.respcode.BizErrorCode;
import com.castle.fortress.common.respcode.GlobalRespCode;
import com.castle.fortress.common.utils.CommonUtil;
import com.castle.fortress.common.utils.ConvertUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;


/**
 * 登录
 * @author castle
 */
@Api(tags = "登录控制器")
@Controller
public class LoginController {
    @Resource
    private SysUserService sysUserService;
    @Resource
    private SysMenuService sysMenuService;
    @Resource
    private SysRoleService sysRoleService;
    @Autowired
    private DataAuthService dataAuthService;
    @Autowired
    private LogSmsService logSmsService;
    @Autowired
    private SmsService smsService;
    @Autowired
    private MemberService membersService;
    /**
     * 系统用户登录界面
     * @return
     */
    @ApiIgnore
    @RequestMapping({"/login"})
    public String toLoginPage(){
        return "admin/login/pages-login.html";
    }

    @ApiOperation("系统用户登录接口")
    @PostMapping("/login")
    @ResponseBody
    public RespBody<Map> doLogin(@RequestParam String username,@RequestParam String password){
        if(CommonUtil.verifyParamNull(username,password)){
            throw new BizException(GlobalRespCode.PARAM_MISSED);
        }
        SysUser user = null;
        boolean isAdmin = false;
        if(GlobalConstants.SUPER_ADMIN_NAME.equals(username)&&GlobalConstants.ROOT_FLAG){
            user=new SysUser();
            user.setId(-1L);
            user.setLoginName(username);
            user.setPassword(PwdUtil.encode(SecureUtil.md5(GlobalConstants.ROOT_PWD)));
            //校验密码
            if(!PwdUtil.matches(password,user.getPassword())){
                throw new BizException(BizErrorCode.PWD_ERROR);
            }
            // 赋予角色
            List<SysRole> roleList = new ArrayList<>();
            SysRole sr=new SysRole();
            sr.setName("超级管理员");
            sr.setId(-1L);
            sr.setIsAdmin(YesNoEnum.YES.getCode());
            roleList.add(sr);
            user.setRoles(roleList);
            user.setIsSuperAdmin(true);
            isAdmin = true;
        }else{
            List<SysUser> users=sysUserService.queryByLoginName(username);
            if(users == null || users.size() != 1){
                throw new BizException(BizErrorCode.PWD_ERROR);
            }
            user=users.get(0);
            //校验密码
            if(!PwdUtil.matches(password,user.getPassword())){
                throw new BizException(BizErrorCode.PWD_ERROR);
            }
            // 赋予角色
            List<SysRole> roleList = sysRoleService.queryListByUser(user.getId());
            user.setRoles(roleList);
            // 赋予角色
            List<Long> roleIds=new ArrayList<>();
            for (SysRole role : roleList) {
                roleIds.add(role.getId());
                if(role.getIsAdmin().equals(YesNoEnum.YES.getCode())){
                    isAdmin = true;
                }
            }
        }

        if(!isAdmin){
            //数据权限部门
            List<Long> authDept = dataAuthService.getAuthDeptsList(user.getId(),null);
            if(user.getPostId()!=null){
                //下级岗位
                List<Long> subPost = dataAuthService.getSubPostList(user.getPostId());
                user.setSubPost(subPost);
            }
            //无职位限制 则部门内不限
            if(user.getPostId() == null || DataPermissionPostEnum.NO_LIMIT.getCode().equals(user.getPostDataAuth())){
                authDept = dataAuthService.getAuthDeptsList(user.getId(),user.getDeptId());
            }
            user.setAuthDept(authDept);
        }
        Date now=new Date();
        Date tokenExpiration= DateUtil.offsetMinute(now, GlobalConstants.TOKEN_EXPIRATION_MINUTE);
        String authToken= TokenUtil.createToken(sysUserToCastleUser(user),tokenExpiration);
        Date refreshTokenExpiration= DateUtil.offsetHour(now, GlobalConstants.REFRESH_TOKEN_EXPIRATION_HOUR);
        String refreshToken = TokenUtil.createRefreshToken(sysUserToCastleUser(user),refreshTokenExpiration);
        Map<String,Object> map=new HashMap<>();
        map.put("token",authToken);
        map.put("expiredTime",DateUtil.format(tokenExpiration,"yyyy-MM-dd HH:mm:ss"));
        map.put("refreshToken",refreshToken);
        RespBody<Map> respBody=RespBody.data(map);
        return respBody;

    }

    /**
     * 系统用户转化为CastleUserDetail类型
     * @param user
     * @return
     */
    private CastleUserDetail sysUserToCastleUser(SysUser user){
        CastleUserDetail userDetail=null;
        if(user!=null){
            userDetail = new CastleUserDetail();
            userDetail.setId(user.getId());
            userDetail.setUsername(user.getLoginName());
            userDetail.setAvatar(user.getAvatar());
            userDetail.setUserType(UserTypeEnum.SYS_USER.getName());
            userDetail.setDeptId(user.getDeptId());
            userDetail.setNickname(user.getNickname());
            userDetail.setIsSuperAdmin(user.getIsSuperAdmin());
            userDetail.setPostId(user.getPostId());
            userDetail.setRealName(user.getRealName());
            userDetail.setRoles(user.getRoles());
            userDetail.setAuthDept(user.getAuthDept());
            userDetail.setSubPost(user.getSubPost());
            userDetail.setPostDataAuth(user.getPostDataAuth());
        }
        return userDetail;
    }
    @ApiOperation("获取当前登录的系统用户信息")
    @GetMapping("/userInfo")
    @ResponseBody
    public RespBody<SysUser> userInfo(){
        SysUser sysUser = WebUtil.currentUser();
        if(sysUser == null){
            throw new BizException(GlobalRespCode.NO_LOGIN_ERROR);
        }
        CastleUserDetail userDetail = sysUserToCastleUser(sysUser);
        Set<String> permissions = sysMenuService.getPermissions(userDetail);
        sysUser.setPermission(permissions);
        return RespBody.data(sysUser);
    }

    @ApiOperation("通过refreshToken刷新token")
    @PostMapping("/refreshToken")
    @ResponseBody
    public RespBody<Map> refreshToken(HttpServletRequest request){
        String refreshToken = WebUtil.getRefreshToken(request);
        if(StrUtil.isEmpty(refreshToken)){
            throw new BizException(GlobalRespCode.TOKEN_INVALID_ERROR);
        }
        CastleUserDetail userDetail = TokenUtil.parseToken(refreshToken);
        if(userDetail!=null){
            SysUser user = null;
            boolean isAdmin = false;
            if(userDetail.getId().equals(-1L)&&GlobalConstants.ROOT_FLAG){
                user=new SysUser();
                user.setId(-1L);
                user.setLoginName(GlobalConstants.SUPER_ADMIN_NAME);
                user.setPassword(PwdUtil.encode(SecureUtil.md5(GlobalConstants.ROOT_PWD)));
                // 赋予角色
                List<SysRole> roleList = new ArrayList<>();
                SysRole sr=new SysRole();
                sr.setName("超级管理员");
                sr.setId(-1L);
                sr.setIsAdmin(YesNoEnum.YES.getCode());
                roleList.add(sr);
                user.setRoles(roleList);
                user.setIsSuperAdmin(true);
                isAdmin = true;
            }else{
                user=sysUserService.getById(userDetail.getId());
                if(user == null){
                    throw new BizException(GlobalRespCode.TOKEN_INVALID_ERROR);
                }
                // 赋予角色
                List<SysRole> roleList = sysRoleService.queryListByUser(user.getId());
                user.setRoles(roleList);
                // 赋予角色
                List<Long> roleIds=new ArrayList<>();
                for (SysRole role : roleList) {
                    roleIds.add(role.getId());
                    if(role.getIsAdmin().equals(YesNoEnum.YES.getCode())){
                        isAdmin = true;
                    }
                }
            }
            if(!isAdmin){
                //数据权限部门
                List<Long> authDept = dataAuthService.getAuthDeptsList(user.getId(),null);
                if(user.getPostId()!=null){
                    //下级岗位
                    List<Long> subPost = dataAuthService.getSubPostList(user.getPostId());
                    user.setSubPost(subPost);
                }
                //无职位限制 则部门内不限
                if(user.getPostId() == null || DataPermissionPostEnum.NO_LIMIT.getCode().equals(user.getPostDataAuth())){
                    authDept = dataAuthService.getAuthDeptsList(user.getId(),user.getDeptId());
                }
                user.setAuthDept(authDept);
            }
            Date now=new Date();
            Date tokenExpiration= DateUtil.offsetMinute(now, GlobalConstants.TOKEN_EXPIRATION_MINUTE);
            String authToken= TokenUtil.createToken(sysUserToCastleUser(user),tokenExpiration);
            Date refreshTokenExpiration= DateUtil.offsetHour(now, GlobalConstants.REFRESH_TOKEN_EXPIRATION_HOUR);
            String refreshTokenNew = TokenUtil.createRefreshToken(sysUserToCastleUser(user),refreshTokenExpiration);
            Map<String,Object> map=new HashMap<>();
            map.put("token",authToken);
            map.put("expiredTime",DateUtil.format(tokenExpiration,"yyyy-MM-dd HH:mm:ss"));
            map.put("refreshToken",refreshTokenNew);
            RespBody<Map> respBody=RespBody.data(map);
            return respBody;
        }else{
            throw new BizException(GlobalRespCode.TOKEN_INVALID_ERROR);
        }
    }

    @ApiOperation("Member通过refreshToken刷新token")
    @PostMapping("/memberRefreshToken")
    @ResponseBody
    public RespBody<Map> memberRefreshToken(HttpServletRequest request){
        String refreshToken = WebUtil.getRefreshToken(request);
        if(StrUtil.isEmpty(refreshToken)){
            throw new BizException(GlobalRespCode.TOKEN_INVALID_ERROR);
        }
        CastleUserDetail userDetail = TokenUtil.parseToken(refreshToken);
        if(userDetail!=null){
            MemberEntity memberEntity = membersService.getById(userDetail.getId());

            Date now=new Date();
            Date tokenExpiration= DateUtil.offsetMinute(now, GlobalConstants.TOKEN_EXPIRATION_MINUTE);
            String authToken= TokenUtil.createToken(memberToCastleUser(memberEntity),tokenExpiration);
            Date refreshTokenExpiration= DateUtil.offsetHour(now, GlobalConstants.REFRESH_TOKEN_EXPIRATION_HOUR);
            String newRefreshToken = TokenUtil.createRefreshToken(memberToCastleUser(memberEntity),refreshTokenExpiration);
            Long serialNumToken = IdWorker.getId();
            Map<String,String> tokenMap=new HashMap<>();
            tokenMap.put("token",authToken);
            tokenMap.put("expiredTime",DateUtil.format(tokenExpiration,"yyyy-MM-dd HH:mm:ss"));
            tokenMap.put("refreshToken",newRefreshToken);
            tokenMap.put("id",memberEntity.getId()+"");
            tokenMap.put("status","login");
            Map<String,Object> map=new HashMap<>();
            map.put("token",authToken);
            map.put("expiredTime",DateUtil.format(tokenExpiration,"yyyy-MM-dd HH:mm:ss"));
            map.put("refreshToken",newRefreshToken);
            RespBody<Map> respBody=RespBody.data(map);
            return  respBody;
        }else{
            throw new BizException(GlobalRespCode.TOKEN_INVALID_ERROR);
        }
    }

    /**
     * 微信登录
     * @return
     */
    @PostMapping("/wxLogin")
    @ApiOperation(value = "微信登录")
    @ResponseBody
    public void wxLogin(@RequestParam String code) {
        System.out.println("【wxLogin】begin");
        // TODO 微信信息应该配置
        String appId = "微信appId";
        String appSecret = "微信秘钥";
        //通过code获取access_code
        Map<String, String> queryParams = new HashMap<>();
        queryParams.put("appid", appId);
        queryParams.put("secret", appSecret);
        queryParams.put("code", code);
        queryParams.put("grant_type", "authorization_code");
        String accessCodeData = HttpUtil.doRequest("get", "https://api.weixin.qq.com/sns/oauth2/access_token", new HashMap<>(), queryParams, null);
        JSONObject accessCodeDataJson = new JSONObject(accessCodeData);
        //获取到的token  和 openId
        String accessCode = accessCodeDataJson.getStr("access_token");
        String openId = accessCodeDataJson.getStr("openid");
        //用token 和 openId 获取微信用户信息
        Map<String, String> userInfoQueryParams = new HashMap<>();
        userInfoQueryParams.put("access_token", accessCode);
        userInfoQueryParams.put("openid", openId);
        String userInfoData = HttpUtil.doRequest("get", "https://api.weixin.qq.com/sns/userinfo", new HashMap<>(), userInfoQueryParams, null);
        JSONObject userInfoDataJson = new JSONObject(userInfoData);
        System.err.println("userInfoDataJson:"+userInfoDataJson);
        String headImgUrl = userInfoDataJson.getStr("headimgurl");
        String nickname = userInfoDataJson.getStr("nickname");
        String unionId = userInfoDataJson.getStr("unionid");
        //todo 判断是否已有会员。
    }



    /**
     * 会员验证码注册或登录
     * @param membersDto 会员信息
     * @return
     */
    @ApiOperation("会员验证码注册或登录")
    @PostMapping("/api/member/codeLogin")
    @ResponseBody
    public RespBody<Map> register(@RequestBody MemberDto membersDto){
        if(membersDto == null ){
            throw new BizException(GlobalRespCode.PARAM_MISSED);
        }
        // 验证验证码
        String smsCode = membersDto.getSmsCode();
        if(!"castle".equals(smsCode)){
            Map<String , Object> params = new HashMap<>();
            params.put("mobile" , membersDto.getPhone());
            params.put("status" , LogSmsStatusEnum.VALID.getCode());
            params.put("type" , LogSmsTypeEnum.LOGIN.getCode());
            System.err.println(params);
            List<LogSmsDto> list = logSmsService.getDataList(params);
            if(list.size() > 0){
                LogSmsDto logSmsDto = list.get(0);
                if(!smsCode.equals(logSmsDto.getParams1())){
                    return RespBody.fail("验证码错误");
                }else {
                    // 验证成功 将验证码改为失效
                    logSmsDto.setStatus(LogSmsStatusEnum.INVALID.getCode());
                    logSmsService.updateById(ConvertUtil.transformObj(logSmsDto , LogSmsEntity.class));
                }
            }else {
                return RespBody.fail("验证码错误");
            }
        }
        MemberEntity membersEntity = ConvertUtil.transformObj(membersDto , MemberEntity.class);
        MemberDto membersDto1 = membersService.getByPhone(membersDto.getPhone());
        if(null != membersDto1){
            membersEntity = ConvertUtil.transformObj(membersDto1,MemberEntity.class);
        }else {
            // 用户首次登录
            membersEntity.setNickName(getNickName(8));
            //将手机号作为用户名
            membersEntity.setUserName(membersEntity.getPhone());
            //默认头像 需要自己进行设置
            membersEntity.setAvatar("");
            membersService.save(membersEntity);
        }
        Date now=new Date();
        Date tokenExpiration= DateUtil.offsetMinute(now, GlobalConstants.TOKEN_EXPIRATION_MINUTE);
        String authToken= TokenUtil.createToken(memberToCastleUser(membersEntity),tokenExpiration);
        Date refreshTokenExpiration= DateUtil.offsetHour(now, GlobalConstants.REFRESH_TOKEN_EXPIRATION_HOUR);
        String refreshToken = TokenUtil.createRefreshToken(memberToCastleUser(membersEntity),refreshTokenExpiration);

        Map<String,Object> map=new HashMap<>();
        map.put("token",authToken);
        map.put("expiredTime",DateUtil.format(tokenExpiration,"yyyy-MM-dd HH:mm:ss"));
        map.put("refreshToken",refreshToken);
        map.put("id",membersEntity.getId());
        RespBody<Map> respBody=RespBody.data(map);
        return  respBody;

    }


    @PostMapping("/api/sendCode")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "mobile" , value = "手机号" , required = true,paramType = "body" , dataType = "String"),
            @ApiImplicitParam(name = "type" , value = "短信类型 0注册验证码 1登录验证码" , required = true,paramType = "body" , dataType = "String")
    })
    @ApiOperation("发送短信验证码")
    @ResponseBody
    public RespBody send(@ApiIgnore @RequestBody Map<String,String> map){
        String mobile = map.get("mobile");
        if(map == null  || StrUtil.isEmpty(mobile) || StrUtil.isEmpty(map.get("type"))){
            throw new BizException(GlobalRespCode.PARAM_MISSED);
        }
        Integer type = Integer.valueOf(map.get("type"));
        // 6位随机验证码
        String code = String.valueOf((int)(Math.random() * 1000000));

        // 阿里云短信参数为json格式  腾讯云为逗号隔开字符串
//        String params = "{ 1 : \""+code+"\" , 2: \"5\" }";
        // 短信编码 表中配置
        String smsCode = null;
        RespBody result = null;
        if(type == LogSmsTypeEnum.LOGIN.getCode()){
            // 登录验证码  短信编码  暂用  1001
            smsCode = "1001";
//            String params = code+",5";
            String params = "{\"code\":"+code+"}";
            result = smsService.send("1001", mobile , params);
        }else if (type == LogSmsTypeEnum.RES.getCode()){
            // 注册验证码   暂时不使用
//            smsCode = "1001";
//            result = smsService.send("1001", mobile , params);
        }else {
            return RespBody.fail("短信类型错误");
        }

        if(result.getCode() == GlobalRespCode.SUCCESS.getCode()){
            // 若当前已有生效短信 改为不生效
            Map<String , Object> smsParams = new HashMap<>();
            smsParams.put("mobile" , mobile);
            smsParams.put("status" , LogSmsStatusEnum.VALID.getCode());
            smsParams.put("type" , type);
            List<LogSmsDto> list = logSmsService.getDataList(smsParams);
            if(list.size() > 0){
                list.forEach(e->e.setStatus(LogSmsStatusEnum.INVALID.getCode()));
                logSmsService.updateBatchById(ConvertUtil.transformObjList(list,LogSmsEntity.class));
            }
            // 短信发送成功 保存记录
            LogSmsEntity logSmsEntity = new LogSmsEntity();
            logSmsEntity.setSmsCode(smsCode);
            logSmsEntity.setMobile(mobile);
            logSmsEntity.setType(type);
            logSmsEntity.setCreateDate(new Date());
            logSmsEntity.setParams1(code);
            logSmsEntity.setStatus(LogSmsStatusEnum.VALID.getCode());
            logSmsService.save(logSmsEntity);
        }

        return result;
    }


    //生成随机数字和字母昵称
    private String getNickName(int length) {

        StringBuilder val = new StringBuilder();
        Random random = new Random();

        //参数length，表示生成几位随机数
        for(int i = 0; i < length; i++) {

            String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";
            //输出字母还是数字
            if( "char".equalsIgnoreCase(charOrNum) ) {
                //输出是大写字母还是小写字母
                int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;
                val.append((char) (random.nextInt(26) + temp));
            } else {
                val.append(String.valueOf(random.nextInt(10)));
            }
        }
        return "用户" +  val.toString();
    }


    /**
     * 会员转化为CastleUserDetail类型
     * @param membersEntity
     * @return
     */
    private CastleUserDetail memberToCastleUser(MemberEntity membersEntity){
        CastleUserDetail userDetail=null;
        if(membersEntity!=null){
            userDetail = new CastleUserDetail();
            userDetail.setId(membersEntity.getId());
            userDetail.setUsername(membersEntity.getPhone());
            userDetail.setPhone(membersEntity.getPhone());
            userDetail.setNickname(membersEntity.getNickName());
            userDetail.setAvatar(membersEntity.getAvatar());
            userDetail.setUserType(UserTypeEnum.MEMBER.getName());
        }
        return userDetail;
    }

}
